#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>
#include <signal.h>
#include <errno.h>

#define SERIAL_PORT "/dev/ttyAMA0"
#define BAUDRATE B115200
#define BUFFER_SIZE 1024
#define TIME_STR_MAX_LEN 20
#define READ_TIMEOUT 10  // 10 * 100ms = 1s
#define MIN_READ_CHARS 0 // 最小读取字符数

int serial_fd = -1;  // 全局串口文件描述符，用于信号处理

// 信号处理函数，用于优雅退出
void handle_signal(int signum) {
    if (serial_fd != -1) {
        close(serial_fd);
        printf("\n串口已关闭\n");
    }
    printf("程序已退出，信号值: %d\n", signum);
    exit(0);
}

// 打开并配置串口，返回文件描述符，失败返回-1
int open_and_configure_serial() {
    // 打开串口设备
    int fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY);
    if (fd == -1) {
        fprintf(stderr, "错误：打开串口 %s 失败，原因：%s\n", SERIAL_PORT, strerror(errno));
        return -1;
    }

    // 配置为阻塞模式
    fcntl(fd, F_SETFL, 0);

    // 获取当前串口配置
    struct termios options;
    if (tcgetattr(fd, &options) != 0) {
        fprintf(stderr, "错误：获取串口属性失败，原因：%s\n", strerror(errno));
        close(fd);
        return -1;
    }

    // 设置输入输出波特率
    if (cfsetispeed(&options, BAUDRATE) != 0 || cfsetospeed(&options, BAUDRATE) != 0) {
        fprintf(stderr, "错误：设置波特率失败，原因：%s\n", strerror(errno));
        close(fd);
        return -1;
    }

    // 配置控制模式
    options.c_cflag |= (CLOCAL | CREAD);  // 启用接收，忽略调制解调器控制线
    options.c_cflag &= ~CSIZE;            // 清除数据位设置
    options.c_cflag |= CS8;               // 8位数据位
    options.c_cflag &= ~PARENB;           // 无奇偶校验
    options.c_cflag &= ~CSTOPB;           // 1位停止位
    options.c_cflag &= ~CRTSCTS;          // 禁用硬件流控制

    // 配置输入模式
    options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);

    // 配置输出模式
    options.c_oflag &= ~OPOST;
    options.c_oflag &= ~ONLCR;

    // 配置本地模式
    options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

    // 设置读取超时和最小字符数
    options.c_cc[VTIME] = READ_TIMEOUT;
    options.c_cc[VMIN] = MIN_READ_CHARS;

    // 刷新输入输出缓冲区
    tcflush(fd, TCIFLUSH);

    // 应用配置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        fprintf(stderr, "错误：配置串口失败，原因：%s\n", strerror(errno));
        close(fd);
        return -1;
    }

    printf("成功打开串口 %s (波特率: %d, 8N1)\n", SERIAL_PORT, (int)BAUDRATE);
    return fd;
}

// 获取当前时间字符串
void get_current_time_str(char *time_str, size_t max_len) {
    time_t now;
    struct tm *tm_info;
    time(&now);
    tm_info = localtime(&now);
    strftime(time_str, max_len, "%H:%M:%S", tm_info);
}

int main() {
    // 注册信号处理函数，捕获Ctrl+C和终止信号
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);

    // 打开并配置串口
    serial_fd = open_and_configure_serial();
    if (serial_fd == -1) {
        return EXIT_FAILURE;
    }

    printf("等待接收数据，按 Ctrl+C 停止...\n");
    printf("调试信息：串口文件描述符 = %d\n", serial_fd);

    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    char time_str[TIME_STR_MAX_LEN];

    while (1) {
        // 读取串口数据
        bytes_read = read(serial_fd, buffer, sizeof(buffer) - 1);
        
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            get_current_time_str(time_str, TIME_STR_MAX_LEN);

            // 打印接收字节数（调试用）
            printf("\n[%s] 接收到 %ld 字节数据：\n", time_str, bytes_read);

            // 判断是否为文本数据
            int is_text = 1;
            for (int i = 0; i < bytes_read; i++) {
                if ((buffer[i] < 32 && buffer[i] != '\n' && buffer[i] != '\r' && buffer[i] != '\t') || buffer[i] > 126) {
                    is_text = 0;
                    break;
                }
            }

            if (is_text) {
                printf("文本内容：%s\n", buffer);
            } else {
                printf("十六进制：");
                for (int i = 0; i < bytes_read; i++) {
                    printf("%02X ", (unsigned char)buffer[i]);
                }
                printf("\n");
            }
        } else if (bytes_read < 0) {
            fprintf(stderr, "错误：读取串口数据失败，原因：%s\n", strerror(errno));
            break;
        }
        
        // 短暂延迟，降低CPU占用
        usleep(10000);  // 10ms
    }

    // 关闭串口
    close(serial_fd);
    printf("串口已关闭\n");
    return EXIT_SUCCESS;
}